package top.szhome.develop.service.impl;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import top.szhome.develop.entity.*;
import top.szhome.develop.generator.Context;
import top.szhome.develop.generator.FileNode;
import top.szhome.develop.generator.FileSystem;
import top.szhome.develop.generator.Generator;
import top.szhome.develop.service.*;
import top.szhome.framework.core.tools.constant.SzhomeConstant;
import top.szhome.framework.core.tools.exception.SzhomeException;
import top.szhome.framework.core.tools.util.AssertUtil;

import java.io.IOException;
import java.io.OutputStream;
import java.util.*;

/**
 * 代码生成器服务实现
 *
 * @author 唐加利
 * @date 2023/8/30
 **/
@Slf4j
@RequiredArgsConstructor
@Service
public class GeneratorServiceImpl implements GeneratorService {

    private final ProjectService projectService;

    private final ModelService modelService;

    private final PropertyService propertyService;

    private final TemplateService templateService;

    private final TemplateSchemaService templateSchemaService;


    @Override
    public List<FileNode> preview(List<Long> modelIds){
        FileSystem fileSystem = new FileSystem();
        for(Long modelId : modelIds){
            try {
                generate(modelId, fileSystem);
            } catch (Exception e) {
                log.error("生成预览代码失败， modelId={}", modelId, e);
            }
        }
        return fileSystem.getRoot();
    }

    @Override
    public FileSystem generate(List<Long> modelIds, OutputStream os){
        FileSystem fs = new FileSystem();
        modelIds.stream().forEach(modelId -> generate(modelId, fs));
        try {
            fs.zip(os);
            return fs;
        } catch (IOException e) {
            throw new SzhomeException("生成代码压缩文件失败", e);
        }
    }

    /**
     * 生成模型文件树
     * @param modelId
     * @param fileSystem
     * @return
     */
    private FileSystem generate(Long modelId, FileSystem fileSystem) {
        Model model = getModel(modelId);
        AssertUtil.notNull(model, "不存在ID为" + modelId + "的模型");

        Project project = projectService.getById(model.getProjectId());
        AssertUtil.notNull(project, "模型" + model.getTableName() + "未配置所属工程，无法生成代码");

        TemplateSchema schema = templateSchemaService.getById(project.getTemplateSchemaId());
        List<Property> properties = propertyService.getProperties(modelId);
        Context context = buildContext(project, model, properties);

        // 获取模板列表，生成代码
        List<Template> templates = templateService.getTemplates(schema.getId());
        Optional.ofNullable(templates).orElse(new ArrayList<>()).stream().forEach(template -> {
            String codes = generate(template, context);
            String file = Generator.getFile(template, context);
            fileSystem.createFile(file, codes);
        });

        return fileSystem;
    }

    @Override
    public String generate(Long modelId, String templateSchemaCode, String templateCode) {
        Model model = getModel(modelId);
        TemplateSchema schema = getTemplateSchema(templateSchemaCode);

        List<Property> properties = propertyService.getProperties(modelId);
        Project project = projectService.getById(model.getProjectId());
        Template template = templateService.getTemplate(schema.getId(), templateCode);
        if(template == null){
            log.error("模板不存在，templateCode={}", templateCode);
            throw new SzhomeException("500", "模板不存在，模板编码：" + templateCode);
        }
        return generate(template, project, model, properties);
    }

    /**
     * 根据模型
     * @param modelId
     * @param template
     * @return
     */
    public String generate(Long modelId, Template template) {
        Model model = modelService.getById(modelId);
        return generate(model, template);
    }

    /**
     * 根据模型
     * @param model
     * @param template
     * @return
     */
    public String generate(Model model, Template template) {
        List<Property> properties = propertyService.getProperties(model.getId());
        Project project = projectService.getById(model.getProjectId());
        return generate(template, project, model, properties);
    }

    /**
     * 生成代码
     * @param template      模板配置
     * @param project       应用工程
     * @param model         当前模型
     * @param properties    当前模型的属性集
     * @return
     */
    public String generate(Template template, Project project, Model model, List<Property> properties) {
        Map<Model, List<Property>> models = new HashMap<>(2);
        models.put(model, properties);
        return generate(template, project, model, properties, models);
    }

    /**
     * 生成代码
     * @param template      模板配置
     * @param project       应用工程
     * @param model         当前模型
     * @param properties    当前模型的属性集
     * @param models        代码生成的全量模型
     * @return 返回生成代码
     */
    public String generate(Template template, Project project, Model model, List<Property> properties, Map<Model, List<Property>> models) {
        Context context = buildContext(project, model, properties, models);
        return generate(template, context);
    }

    /**
     * 生成代码
     * @param template
     * @param context
     * @return
     */
    private String generate(Template template, Context context) {
        try {
            return Generator.getCode(template, context);
        } catch (Exception e) {
            return "代码生成错误，" + e.getStackTrace();
        }
    }

    /**
     * 获取模型
     * @param modelId
     * @return
     */
    private Model getModel(Long modelId){
        Model model = modelService.getById(modelId);
        if(model == null){
            log.error("模型不存在，modelId={}", modelId);
            throw new SzhomeException("500", "模型不存在，模型ID：" + modelId);
        }
        return model;
    }

    /**
     * 获取模板方案
     * @param templateSchemaCode
     * @return
     */
    private TemplateSchema getTemplateSchema(String templateSchemaCode) {
        TemplateSchema schema = templateSchemaService.getTemplateSchema(templateSchemaCode);
        if(schema == null){
            log.error("模板方案不存在，templateSchemaCode={}", templateSchemaCode);
            throw new SzhomeException("500", "模板方案不存在，模板方案编码：" + templateSchemaCode);
        }
        return schema;
    }

    /**
     * 构造上下文
     * @param project
     * @param model
     * @param properties
     * @return
     */
    public Context buildContext(Project project, Model model, List<Property> properties) {
        Map<Model, List<Property>> models = new HashMap<>(2);
        models.put(model, properties);
        return buildContext(project, model, properties, models);
    }

    /**
     * 构造上下文
     * @param project
     * @param model
     * @param properties
     * @param models
     * @return
     */
    public Context buildContext(Project project, Model model, List<Property> properties, Map<Model, List<Property>> models) {
        Context context = new Context();
        if(project == null){
            project = new Project();
            project.setBasePkg(SzhomeConstant.BASE_PACKAGE);
        }

        context.setProject(project)
                .setModel(model)
                .setProperties(properties)
                .setModels(models);
        return context;
    }

}
